/******************************************************************************
Tinapoc Project - The Java Reverse Engineering Toolkit
Copyright (C) 2005  Daniel Reynaud-Plantey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
******************************************************************************/


package tina.classparsing;

import java.io.RandomAccessFile;
import java.io.IOException;
import java.io.StringWriter;
import java.io.PrintWriter;

public class TinaBytecode {
    public enum Mnemonic {
        nop,
        aconst_null,
        iconst_m1,
        iconst_0,
        iconst_1,
        iconst_2,
        iconst_3,
        iconst_4,
        iconst_5,
        lconst_0,
        lconst_1,
        fconst_0,
        fconst_1,
        fconst_2,
        dconst_0,
        dconst_1,
        bipush,
        sipush,
        ldc,
        ldc_w,
        ldc2_w,
        iload,
        lload,
        fload,
        dload,
        aload,
        iload_0,
        iload_1,
        iload_2,
        iload_3,
        lload_0,
        lload_1,
        lload_2,
        lload_3,
        fload_0,
        fload_1,
        fload_2,
        fload_3,
        dload_0,
        dload_1,
        dload_2,
        dload_3,
        aload_0,
        aload_1,
        aload_2,
        aload_3,
        iaload,
        laload,
        faload,
        daload,
        aaload,
        baload,
        caload,
        saload,
        istore,
        lstore,
        fstore,
        dstore,
        astore,
        istore_0,
        istore_1,
        istore_2,
        istore_3,
        lstore_0,
        lstore_1,
        lstore_2,
        lstore_3,
        fstore_0,
        fstore_1,
        fstore_2,
        fstore_3,
        dstore_0,
        dstore_1,
        dstore_2,
        dstore_3,
        astore_0,
        astore_1,
        astore_2,
        astore_3,
        iastore,
        lastore,
        fastore,
        dastore,
        aastore,
        bastore,
        castore,
        sastore,
        pop,
        pop2,
        dup,
        dup_x1,
        dup_x2,
        dup2,
        dup2_x1,
        dup2_x2,
        swap,
        iadd,
        ladd,
        fadd,
        dadd,
        isub,
        lsub,
        fsub,
        dsub,
        imul,
        lmul,
        fmul,
        dmul,
        idiv,
        ldiv,
        fdiv,
        ddiv,
        irem,
        lrem,
        frem,
        drem,
        ineg,
        lneg,
        fneg,
        dneg,
        ishl,
        lshl,
        ishr,
        lshr,
        iushr,
        lushr,
        iand,
        land,
        ior,
        lor,
        ixor,
        lxor,
        iinc,
        i2l,
        i2f,
        i2d,
        l2i,
        l2f,
        l2d,
        f2i,
        f2l,
        f2d,
        d2i,
        d2l,
        d2f,
        i2b,
        i2c,
        i2s,
        lcmp,
        fcmpl,
        fcmpg,
        dcmpl,
        dcmpg,
        ifeq,
        ifne,
        iflt,
        ifge,
        ifgt,
        ifle,
        if_icmpeq,
        if_icmpne,
        if_icmplt,
        if_icmpge,
        if_icmpgt,
        if_icmple,
        if_acmpeq,
        if_acmpne,
        _goto,
        jsr,
        ret,
        tableswitch,
        lookupswitch,
        ireturn,
        lreturn,
        freturn,
        dreturn,
        areturn,
        _return,
        getstatic,
        putstatic,
        getfield,
        putfield,
        invokevirtual,
        invokespecial,
        invokestatic,
        invokeinterface,
        xxxunusedxxx,
        _new,
        newarray,
        anewarray,
        arraylength,
        athrow,
        checkcast,
        _instanceof,
        monitorenter,
        monitorexit,
        wide,
        multianewarray,
        ifnull,
        ifnonnull,
        goto_w,
        jsr_w,
        breakpoint,
        impdep1,
        impdep2;
    }

    private TinaConstantPool cp;
    private RandomAccessFile in;
    private int offset,        // the offset in the current code array
                opcode;
    private String label,      // the jasmin label for this opcode, null if there is no label
                   stringreg;  // the string representation of this bytecode (and args)
    private Mnemonic mnemonic; // mnemonic associated with the opcode

// arguments of the bytecode
    private Integer index,
                    defaultb, // for tableswitch
                    value, 
                    value2;
    private int[] offsets;  // for tableswitch and lookupswitch
    private String comment;
    private Mnemonic wide_target;

    public TinaBytecode(int opcode) throws IOException {
        this(opcode, -1, null, null);
    }

    public TinaBytecode(int opcode, int offset, RandomAccessFile in,
                        TinaConstantPool cp) throws IOException {
        this.opcode = opcode;
        this.offset = offset;
        this.in = in;
        this.mnemonic = getMnemonic(opcode);
        this.cp = cp;
        offsets = new int[0];
        init();
    }

    private void init() throws IOException {
        int padding;
        if(in==null || cp==null)  // can't init, "standalone" bytecode
            return;
        switch(mnemonic) {
// 1 byte opcodes
    // index
            case ldc :
                index = new Integer(in.readUnsignedByte());
                break;

    // signed values
            case bipush :
                value = new Integer(in.readByte());
                break;

    // unsigned values
            case istore :
            case lstore :
            case fstore :
            case dstore :
            case astore :
            case iload :
            case lload :
            case fload :
            case dload :
            case aload :
            case ret :
                value = new Integer(in.readUnsignedByte());
                break;

    // special tag 
            case newarray :
                value = new Integer(in.readByte());
                switch(value) {
                    case 4  : comment = "T_BOOLEAN";
                            break;
                    case 5  : comment = "T_CHAR";
                            break;
                    case 6  : comment = "T_FLOAT";
                            break;
                    case 7  : comment = "T_DOUBLE";
                            break;
                    case 8  : comment = "T_BYTE";
                            break;
                    case 9  : comment = "T_SHORT";
                            break;
                    case 10 : comment = "T_INT";
                            break;
                    case 11 : comment = "T_LONG";
                            break;
                    default : comment = "TINAPOC_ERROR_TYPE_UNKNOWN";
                }
                break;




// 2 byte opcodes
    // indices (unsigned)
            case anewarray :
            case checkcast :
            case getstatic :
            case putstatic :
            case getfield :
            case putfield :
            case _instanceof :
            case invokevirtual :
            case invokespecial :
            case invokestatic :
            case ldc_w :
            case ldc2_w :
            case _new :
                index = in.readUnsignedShort();
                break;

    // jump addresses (signed)
            case sipush : // not a jump, but signed anyway
            case ifeq :
            case ifne :
            case iflt :
            case ifge :
            case ifgt :
            case ifle :
            case if_icmpeq :
            case if_icmpne :
            case if_icmplt :
            case if_icmpge :
            case if_icmpgt :
            case if_icmple :
            case if_acmpeq :
            case if_acmpne :
            case _goto : 
            case jsr :   
            case ifnull :
            case ifnonnull :
                value = new Integer(in.readShort());
                break;

    // 2 single 
            case iinc :
                value = new Integer(in.readUnsignedByte());
                value2 = new Integer(in.readByte());
                break;




// 3 byte opcodes
            case multianewarray :
                value = in.readUnsignedShort();
                value2 = new Integer(in.readUnsignedByte());
                break;



// 4 byte opcodes
    // jump address (signed)
            case goto_w :
            case jsr_w :
                value = in.readInt();
                break;

    // special values
            case invokeinterface :
                index = new Integer(in.readShort());
                value = new Integer(in.readUnsignedByte());
                value2 = new Integer(in.readByte());
                if(value2!=0) 
                    comment = "expecting 0";
                break;



// variable length opcodes
            case tableswitch :
                padding = 3 - (offset % 4);
                for(int i=0; i<padding; i++)
                    in.readByte();     // padding
                defaultb   = in.readInt(); // default
                value      = in.readInt(); // low
                value2     = in.readInt(); // high
                if(value>value2) comment = "low has to be <= high";
                else {
                    offsets = new int[value2-value+1];
                    for(int i=0; i<offsets.length; i++)
                        offsets[i] = in.readInt();
                }
                break;

            case lookupswitch :
                padding = 3 - (offset % 4);
                for(int i=0; i<padding; i++)
                    in.readByte();     // padding
                value  = in.readInt(); // default
                value2 = in.readInt(); // npairs
                if(value2>=0) {
                    offsets = new int[2*value2];
                    for(int i=0; i<offsets.length; i++)
                        offsets[i] = in.readInt();
                } else comment = "npairs has to be >= 0";
                break;

 
            case wide :
                value = new Integer(in.readUnsignedByte());
                wide_target = getMnemonic(value);
                value = in.readUnsignedShort();
                if(wide_target==Mnemonic.iinc) 
                    value2 = new Integer(in.readShort());
                break;



// 0 byte opcodes :
            case nop :
            case iaload :
            case laload :
            case faload :
            case daload :
            case aaload :
            case baload :
            case caload :
            case saload :
            case aconst_null :
            case iload_0 :
            case iload_1 :
            case iload_2 :
            case iload_3 :
            case lload_0 :
            case lload_1 :
            case lload_2 :
            case lload_3 :
            case fload_0 :
            case fload_1 :
            case fload_2 :
            case fload_3 :
            case dload_0 :
            case dload_1 :
            case dload_2 :
            case dload_3 :
            case aload_0 :
            case aload_1 :
            case aload_2 :
            case aload_3 :
            case ireturn :
            case lreturn :
            case freturn :
            case dreturn :
            case areturn :
            case _return :
            case arraylength :
            case istore_0 :
            case istore_1 :
            case istore_2 :
            case istore_3 :
            case lstore_0 :
            case lstore_1 :
            case lstore_2 :
            case lstore_3 :
            case fstore_0 :
            case fstore_1 :
            case fstore_2 :
            case fstore_3 :
            case dstore_0 :
            case dstore_1 :
            case dstore_2 :
            case dstore_3 :
            case astore_0 :
            case astore_1 :
            case astore_2 :
            case astore_3 :
            case iastore :
            case lastore :
            case fastore :
            case dastore :
            case aastore :
            case bastore :
            case castore :
            case sastore :
            case athrow :
            case iconst_m1 :
            case iconst_0 :
            case iconst_1 :
            case iconst_2 :
            case iconst_3 :
            case iconst_4 :
            case iconst_5 :
            case lconst_0 :
            case lconst_1 :
            case fconst_0 :
            case fconst_1 :
            case fconst_2 :
            case dconst_0 :
            case dconst_1 :
            case dcmpl :
            case dcmpg :
            case swap :
            case iadd :
            case ladd :
            case fadd :
            case dadd :
            case isub :
            case lsub :
            case fsub :
            case dsub :
            case imul :
            case lmul :
            case fmul :
            case dmul :
            case idiv :
            case ldiv :
            case fdiv :
            case ddiv :
            case irem :
            case lrem :
            case frem :
            case drem :
            case ineg :
            case lneg :
            case fneg :
            case dneg :
            case pop :
            case pop2 :
            case dup :
            case dup_x1 :
            case dup_x2 :
            case dup2 :
            case dup2_x1 :
            case dup2_x2 :
            case i2l :
            case i2f :
            case i2d :
            case l2i :
            case l2f :
            case l2d :
            case f2i :
            case f2l :
            case f2d :
            case d2i :
            case d2l :
            case d2f :
            case i2b :
            case i2c :
            case i2s :
            case lcmp :
            case fcmpl :
            case fcmpg :
            case ishl :
            case lshl :
            case ishr :
            case lshr :
            case iushr :
            case lushr :
            case iand :
            case land :
            case ior :
            case lor :
            case ixor :
            case lxor :
            case monitorenter :
            case monitorexit :
            case xxxunusedxxx :
            case breakpoint :
            case impdep1 :
            case impdep2 :
                break; // do nothing
            default :
                System.err.println("TinaBytecode Error : should never have reached this point");
        }
    }

    public String toString() {
        String result = null;
        try {
            result = toString(false, "; ", false);
        } catch(TinaConstantPoolException e) {
            result = "TINAPOC_UNEXPECTED_ERROR"; //should never happend
        }
        return result;
    }

    public String smartDisplay(String indent, boolean printoffset)
                                 throws TinaConstantPoolException {
        return toString(true, indent, printoffset);
    }

    public String toString(boolean smart, String indent, boolean printoffset)
                              throws TinaConstantPoolException {
        StringWriter buf = new StringWriter();
        PrintWriter p = new PrintWriter(buf);
        p.printf(indent+"%4d: "+clean(mnemonic)+" ", offset);

        if(mnemonic==Mnemonic.wide)
            p.print(wide_target+" ");
        if(index!=null) {
            p.print(index+" ");
            if(smart)
                p.print("("+cp.elementAt(index).smartDisplay()+") ");
        }
        if(defaultb!=null)
            p.print(defaultb+" ");
        if(value!=null)
            p.print(value+" ");
        if(value2!=null)
            p.print(value2+" ");
        if(comment!=null)
            p.print(comment+" ");

        String ni = printoffset?"          ":"    ";
        if(mnemonic==Mnemonic.lookupswitch) {
            for(int i=0; i<offsets.length; i++) {
                p.println();
                p.print(indent+ni+offsets[i++]+" -> "+offsets[i]);
            }
        }

        if(mnemonic==Mnemonic.tableswitch) {
            for(int i=0; i<offsets.length; i++) {
                p.println();
                p.print(indent+ni+offsets[i]);
            }
        }
        return buf.toString();
    }

// removes '_' if it is the first char of m
    private String clean(Mnemonic m) {
        String str = m.toString();
        if(str.startsWith("_"))
            return str.substring(1, str.length());
        else
            return str;
    }

    public static Mnemonic getMnemonic(int opcode) {
        switch(opcode) {
            case 0 : return Mnemonic.nop;
            case 1 : return Mnemonic.aconst_null;
            case 2 : return Mnemonic.iconst_m1;
            case 3 : return Mnemonic.iconst_0;
            case 4 : return Mnemonic.iconst_1;
            case 5 : return Mnemonic.iconst_2;
            case 6 : return Mnemonic.iconst_3;
            case 7 : return Mnemonic.iconst_4;
            case 8 : return Mnemonic.iconst_5;
            case 9 : return Mnemonic.lconst_0;
            case 10 : return Mnemonic.lconst_1;
            case 11 : return Mnemonic.fconst_0;
            case 12 : return Mnemonic.fconst_1;
            case 13 : return Mnemonic.fconst_2;
            case 14 : return Mnemonic.dconst_0;
            case 15 : return Mnemonic.dconst_1;
            case 16 : return Mnemonic.bipush;
            case 17 : return Mnemonic.sipush;
            case 18 : return Mnemonic.ldc;
            case 19 : return Mnemonic.ldc_w;
            case 20 : return Mnemonic.ldc2_w;
            case 21 : return Mnemonic.iload;
            case 22 : return Mnemonic.lload;
            case 23 : return Mnemonic.fload;
            case 24 : return Mnemonic.dload;
            case 25 : return Mnemonic.aload;
            case 26 : return Mnemonic.iload_0;
            case 27 : return Mnemonic.iload_1;
            case 28 : return Mnemonic.iload_2;
            case 29 : return Mnemonic.iload_3;
            case 30 : return Mnemonic.lload_0;
            case 31 : return Mnemonic.lload_1;
            case 32 : return Mnemonic.lload_2;
            case 33 : return Mnemonic.lload_3;
            case 34 : return Mnemonic.fload_0;
            case 35 : return Mnemonic.fload_1;
            case 36 : return Mnemonic.fload_2;
            case 37 : return Mnemonic.fload_3;
            case 38 : return Mnemonic.dload_0;
            case 39 : return Mnemonic.dload_1;
            case 40 : return Mnemonic.dload_2;
            case 41 : return Mnemonic.dload_3;
            case 42 : return Mnemonic.aload_0;
            case 43 : return Mnemonic.aload_1;
            case 44 : return Mnemonic.aload_2;
            case 45 : return Mnemonic.aload_3;
            case 46 : return Mnemonic.iaload;
            case 47 : return Mnemonic.laload;
            case 48 : return Mnemonic.faload;
            case 49 : return Mnemonic.daload;
            case 50 : return Mnemonic.aaload;
            case 51 : return Mnemonic.baload;
            case 52 : return Mnemonic.caload;
            case 53 : return Mnemonic.saload;
            case 54 : return Mnemonic.istore;
            case 55 : return Mnemonic.lstore;
            case 56 : return Mnemonic.fstore;
            case 57 : return Mnemonic.dstore;
            case 58 : return Mnemonic.astore;
            case 59 : return Mnemonic.istore_0;
            case 60 : return Mnemonic.istore_1;
            case 61 : return Mnemonic.istore_2;
            case 62 : return Mnemonic.istore_3;
            case 63 : return Mnemonic.lstore_0;
            case 64 : return Mnemonic.lstore_1;
            case 65 : return Mnemonic.lstore_2;
            case 66 : return Mnemonic.lstore_3;
            case 67 : return Mnemonic.fstore_0;
            case 68 : return Mnemonic.fstore_1;
            case 69 : return Mnemonic.fstore_2;
            case 70 : return Mnemonic.fstore_3;
            case 71 : return Mnemonic.dstore_0;
            case 72 : return Mnemonic.dstore_1;
            case 73 : return Mnemonic.dstore_2;
            case 74 : return Mnemonic.dstore_3;
            case 75 : return Mnemonic.astore_0;
            case 76 : return Mnemonic.astore_1;
            case 77 : return Mnemonic.astore_2;
            case 78 : return Mnemonic.astore_3;
            case 79 : return Mnemonic.iastore;
            case 80 : return Mnemonic.lastore;
            case 81 : return Mnemonic.fastore;
            case 82 : return Mnemonic.dastore;
            case 83 : return Mnemonic.aastore;
            case 84 : return Mnemonic.bastore;
            case 85 : return Mnemonic.castore;
            case 86 : return Mnemonic.sastore;
            case 87 : return Mnemonic.pop;
            case 88 : return Mnemonic.pop2;
            case 89 : return Mnemonic.dup;
            case 90 : return Mnemonic.dup_x1;
            case 91 : return Mnemonic.dup_x2;
            case 92 : return Mnemonic.dup2;
            case 93 : return Mnemonic.dup2_x1;
            case 94 : return Mnemonic.dup2_x2;
            case 95 : return Mnemonic.swap;
            case 96 : return Mnemonic.iadd;
            case 97 : return Mnemonic.ladd;
            case 98 : return Mnemonic.fadd;
            case 99 : return Mnemonic.dadd;
            case 100 : return Mnemonic.isub;
            case 101 : return Mnemonic.lsub;
            case 102 : return Mnemonic.fsub;
            case 103 : return Mnemonic.dsub;
            case 104 : return Mnemonic.imul;
            case 105 : return Mnemonic.lmul;
            case 106 : return Mnemonic.fmul;
            case 107 : return Mnemonic.dmul;
            case 108 : return Mnemonic.idiv;
            case 109 : return Mnemonic.ldiv;
            case 110 : return Mnemonic.fdiv;
            case 111 : return Mnemonic.ddiv;
            case 112 : return Mnemonic.irem;
            case 113 : return Mnemonic.lrem;
            case 114 : return Mnemonic.frem;
            case 115 : return Mnemonic.drem;
            case 116 : return Mnemonic.ineg;
            case 117 : return Mnemonic.lneg;
            case 118 : return Mnemonic.fneg;
            case 119 : return Mnemonic.dneg;
            case 120 : return Mnemonic.ishl;
            case 121 : return Mnemonic.lshl;
            case 122 : return Mnemonic.ishr;
            case 123 : return Mnemonic.lshr;
            case 124 : return Mnemonic.iushr;
            case 125 : return Mnemonic.lushr;
            case 126 : return Mnemonic.iand;
            case 127 : return Mnemonic.land;
            case 128 : return Mnemonic.ior;
            case 129 : return Mnemonic.lor;
            case 130 : return Mnemonic.ixor;
            case 131 : return Mnemonic.lxor;
            case 132 : return Mnemonic.iinc;
            case 133 : return Mnemonic.i2l;
            case 134 : return Mnemonic.i2f;
            case 135 : return Mnemonic.i2d;
            case 136 : return Mnemonic.l2i;
            case 137 : return Mnemonic.l2f;
            case 138 : return Mnemonic.l2d;
            case 139 : return Mnemonic.f2i;
            case 140 : return Mnemonic.f2l;
            case 141 : return Mnemonic.f2d;
            case 142 : return Mnemonic.d2i;
            case 143 : return Mnemonic.d2l;
            case 144 : return Mnemonic.d2f;
            case 145 : return Mnemonic.i2b;
            case 146 : return Mnemonic.i2c;
            case 147 : return Mnemonic.i2s;
            case 148 : return Mnemonic.lcmp;
            case 149 : return Mnemonic.fcmpl;
            case 150 : return Mnemonic.fcmpg;
            case 151 : return Mnemonic.dcmpl;
            case 152 : return Mnemonic.dcmpg;
            case 153 : return Mnemonic.ifeq;
            case 154 : return Mnemonic.ifne;
            case 155 : return Mnemonic.iflt;
            case 156 : return Mnemonic.ifge;
            case 157 : return Mnemonic.ifgt;
            case 158 : return Mnemonic.ifle;
            case 159 : return Mnemonic.if_icmpeq;
            case 160 : return Mnemonic.if_icmpne;
            case 161 : return Mnemonic.if_icmplt;
            case 162 : return Mnemonic.if_icmpge;
            case 163 : return Mnemonic.if_icmpgt;
            case 164 : return Mnemonic.if_icmple;
            case 165 : return Mnemonic.if_acmpeq;
            case 166 : return Mnemonic.if_acmpne;
            case 167 : return Mnemonic._goto ;
            case 168 : return Mnemonic.jsr;
            case 169 : return Mnemonic.ret;
            case 170 : return Mnemonic.tableswitch;
            case 171 : return Mnemonic.lookupswitch;
            case 172 : return Mnemonic.ireturn;
            case 173 : return Mnemonic.lreturn;
            case 174 : return Mnemonic.freturn;
            case 175 : return Mnemonic.dreturn;
            case 176 : return Mnemonic.areturn;
            case 177 : return Mnemonic._return;
            case 178 : return Mnemonic.getstatic;
            case 179 : return Mnemonic.putstatic;
            case 180 : return Mnemonic.getfield;
            case 181 : return Mnemonic.putfield;
            case 182 : return Mnemonic.invokevirtual;
            case 183 : return Mnemonic.invokespecial;
            case 184 : return Mnemonic.invokestatic;
            case 185 : return Mnemonic.invokeinterface;
            case 186 : return Mnemonic.xxxunusedxxx;
            case 187 : return Mnemonic._new;
            case 188 : return Mnemonic.newarray;
            case 189 : return Mnemonic.anewarray;
            case 190 : return Mnemonic.arraylength;
            case 191 : return Mnemonic.athrow;
            case 192 : return Mnemonic.checkcast;
            case 193 : return Mnemonic._instanceof;
            case 194 : return Mnemonic.monitorenter;
            case 195 : return Mnemonic.monitorexit;
            case 196 : return Mnemonic.wide;
            case 197 : return Mnemonic.multianewarray;
            case 198 : return Mnemonic.ifnull;
            case 199 : return Mnemonic.ifnonnull;
            case 200 : return Mnemonic.goto_w;
            case 201 : return Mnemonic.jsr_w;
            case 202 : return Mnemonic.breakpoint;
            case 254 : return Mnemonic.impdep1;
            case 255 : return Mnemonic.impdep2;
            default : return null;
        }
    }
}